﻿using System;
using System.Collections.Generic;
using SCG.General;
using SCG.SolarVengeanceEngine.Orders;
using SCG.SolarVengeanceEngine.StarShips;
using System.Globalization;

namespace SCG.SolarVengeanceEngine
{
    //Represents a logical instance of a Solar Vengeance 6 game
    public class SVGame : IDynamicEvents
    {
        //A RNG accessable to all
        public static Random RNG = new Random();   

        //Common CultureInfo for formatting
        public static CultureInfo CULTURE = new CultureInfo("en-US");

        //Ranged Random number
        public static int Random(int min, int max)
        {
            return RNG.Next(max - min + 1) + min;
        }

        //constructor
        public SVGame(ISVHost host)
        {
            Host = host;
        }

        //Access the host
        public ISVHost Host { get; set; }

        //The scenario object that defined the game       
        public Scenario Scenario
        {
            get
            {
                return _scenario;
            }
            set
            {
                _scenario = value;
            }
        }

        //Access the Scenario name
        public string ScenarioName
        {
            get
            {
                if (Scenario == null)
                    return "";
                else
                    return Scenario.Name;
            }
        }

        //The current impulse
        public int Impulse { get; set; }

        //Was victory achieved?
        public bool Victory { get; set; }    

        //Logical dimensions of the game map
        public int Width
        {
            get
            {
                return _width;
            }
        }
        public int Height
        {
            get
            {
                return _height;
            }
        }

        //Are the specified coordinates in bounds of the game map?
        public bool InBounds(int x, int y)
        {
            return x >= 0 && x < Width && y >= 0 && y < Height;
        }

        //Access all MapObjects
        public List<SVMapObject> MapObjects
        {
            get
            {
                return _mapObjects;
            }
        }

        //Access object at location
        public SVMapObject GetObjectAt(int x, int y)
        {
            if (InBounds(x, y))
                return _map[x, y];
            else
                return null;
        }
        internal void SetObjectAt(int x, int y, SVMapObject svm)
        {
            if (InBounds(x, y))
                _map[x, y] = svm;
        }

        //Lookup an object by its unique ID
        public SVMapObject LookupMapObject(int ID)
        {
            if (ID == -1)
                return null;
            else
                return _masterList[ID];
        }

        //Access the players in the game
        public List<Player> Players
        {
            get
            {
                return _players;
            }
        }
        public Player FindPlayer(string name)
        {
            foreach (Player player in Players)
                if (player.Name == name)
                    return player;
            return null;
        }

        //How many players left
        public int ActiveHumanPlayers
        {
            get
            {
                int n = 0;
                foreach (Player p in Players)
                    if (!p.Eliminated && p.IsHuman)
                        n++;
                return n;
            }
        }

        //The "current" player who is running the session
        public Player CurrentPlayer
        {
            get
            {
                return _currentPlayer;
            }
            set
            {
                _currentPlayer = value;
                SetVisibility();
            }
        }

        //Is it a multi-player game?
        public bool IsMultiPlayer
        {
            get
            {
                int numHuman = 0;
                foreach (Player p in Players)
                    if (p.IsHuman)
                        numHuman++;
                return numHuman > 1;
            }
        }

        //Active MP game?
        public bool IsActiveMultiPlayer
        {
            get
            {
                int numHuman = 0;
                foreach (Player p in Players)
                    if (p.IsHuman)
                        if (!p.Eliminated && !p.DroppedFromGame)
                            numHuman++;
                return numHuman > 1;
            }
        }

        //Is it a Brain duel?
        public bool IsBrainDuel
        {
            get
            {
                foreach (Player p in Players)
                    if (p.IsHuman)
                        return false;
                return true;
            }
        }

        //Access the StarSystems
        public List<StarSystem> StarSystems
        {
            get
            {
                return _starSystems;
            }
        }
        public StarSystem FindStarSystem(string name)
        {
            foreach (StarSystem sys in StarSystems)
                if (sys.Name == name)
                    return sys;
            return null;
        }

        //Access the StarShips
        public List<StarShip> StarShips
        {
            get
            {
                return _starShips;
            }
        }
        public StarShip FindStarShip(string code)
        {
            foreach (StarShip ship in StarShips)
                if (ship.Code == code)
                    return ship;
            return null;
        }

        //Access the wormholes
        public List<Wormhole> Wormholes
        {
            get
            {
                return _wormholes;
            }
        }

        //Access the black holes
        public List<BlackHole> BlackHoles
        {
            get
            {
                return _blackHoles;
            }
        }

        //Access the Pulsars
        public List<Pulsar> Pulsars
        {
            get
            {
                return _pulsars;
            }
        }

        //Access the UDOs
        public List<UDO> UDOs
        {
            get
            {
                return _udos;
            }
        }

        //Access the ScenarioImages
        public List<ScenarioImage> ScenarioImages
        {
            get
            {
                return _scenarioImages;
            }
        }

        //Access Terrain
        public char GetTerrain(int x, int y)
        {
            if (InBounds(x, y))
                return _terrainMap[x, y];
            else
                return ' ';
        }
        public void SetTerrain(int x, int y, char terrain)
        {
            if (x >= 0 && y >= 0 && x < Width && y < Height)
            {
                _terrainMap[x, y] = terrain;
                TerrainChangeKey++;
            }
        }

        //This flag is changed whenever the terrain is modified
        public int TerrainChangeKey { get; set; }

        //Process an impulse!
        //expects a list of strings, each string is a single order
        public string ProcessImpulse(int impulse, List<string> orders)
        {
            _tokStateChanges = new Tokenizer();
            Impulse = impulse;
            CommonPreProcessing();

            _commonProcessing = false;
            foreach (string token in orders)
            {
                SVTokenizer tok = new SVTokenizer(this, token);
                OrderType ot = tok.TokenOrderType;
                switch (ot)
                {
                    case OrderType.BuildShield:
                        {
                            StarSystem system = tok.TokenStarSystem;
                            if (system.Resources > Math.Abs(system.Shields))
                            {
                                system.Shields++;
                                system.Resources -= Math.Abs(system.Shields);
                                SubmitStateChange(StateChange.SetShields, system, system.Shields);
                                SubmitStateChange(StateChange.SetResources, system, system.Resources);
                            }
                        }
                        break;
                    case OrderType.BuildScanner:
                        {
                            StarSystem system = tok.TokenStarSystem;
                            if (system.Resources > Math.Abs(system.Scanners))
                            {
                                system.Scanners++;
                                system.Resources -= Math.Abs(system.Scanners);
                                SubmitStateChange(StateChange.SetScanners, system, system.Scanners);
                                SubmitStateChange(StateChange.SetResources, system, system.Resources);
                            }
                        }
                        break;
                    case OrderType.BuildStarShip:
                        {
                            //extract parameters
                            StarSystem system = tok.TokenStarSystem;
                            int x = tok.TokenInt;
                            int y = tok.TokenInt;
                            StarShipType st = tok.TokenStarShipType;
                            int engines = tok.TokenInt;
                            int primary = tok.TokenInt;
                            bool cloaked = tok.TokenBoolean;
                            BuildStarShip(system, system.Owner, x, y, st, engines, primary, cloaked);
                        }
                        break;
                    case OrderType.ShipBuildStarShip:
                        {
                            //extract parameters
                            ResourceStarShip rss = tok.TokenStarShip as ResourceStarShip;
                            int x = tok.TokenInt;
                            int y = tok.TokenInt;
                            StarShipType st = tok.TokenStarShipType;
                            int engines = tok.TokenInt;
                            int primary = tok.TokenInt;
                            bool cloaked = tok.TokenBoolean;
                            ShipBuildStarShip(rss, x, y, st, engines, primary, cloaked);
                        }
                        break;
                    case OrderType.BuildStarShipAutoPos:
                        {
                            //extract parameters
                            StarSystem system = tok.TokenStarSystem;

                            //see if there's a space open
                            List<SCGPoint> empty = AdjacentPoints(system.Location, true);
                            if (empty.Count > 0)
                            {
                                int x = (int)empty[0].X;
                                int y = (int)empty[0].Y;
                                StarShipType st = tok.TokenStarShipType;
                                int engines = tok.TokenInt;
                                int primary = tok.TokenInt;
                                bool cloaked = tok.TokenBoolean;
                                BuildStarShip(system, system.Owner, x, y, st, engines, primary, cloaked);
                            }
                        }
                        break;
                    case OrderType.ShipBuildStarShipAutoPos:
                        {
                            ResourceStarShip rss = tok.TokenStarShip as ResourceStarShip;
                            if (rss == null)
                                continue;

                            //see if there's a spot open
                            List<SCGPoint> empty = AdjacentPoints(rss.Location, true);
                            if (empty.Count > 0)
                            {
                                int x = (int)empty[0].X;
                                int y = (int)empty[0].Y;
                                StarShipType st = tok.TokenStarShipType;
                                int engines = tok.TokenInt;
                                int primary = tok.TokenInt;
                                bool cloaked = tok.TokenBoolean;
                                ShipBuildStarShip(rss, x, y, st, engines, primary, cloaked);
                            }
                        }
                        break;
                    case OrderType.AutoShields:
                        {
                            StarSystem system = tok.TokenStarSystem;
                            system.AutoShields = tok.TokenBoolean;
                            SubmitStateChange(StateChange.AutoShields, system, system.AutoShields);
                        }
                        break;
                    case OrderType.AutoScanners:
                        {
                            StarSystem system = tok.TokenStarSystem;
                            system.AutoScanners = tok.TokenBoolean;
                            SubmitStateChange(StateChange.AutoScanners, system, system.AutoScanners);
                        }
                        break;
                    case OrderType.AutoStarShip:
                        {
                            StarSystem system = tok.TokenStarSystem;
                            system.AutoStarShip = tok.TokenBoolean;
                            SubmitStateChange(StateChange.AutoStarShip, system, system.AutoStarShip);
                        }
                        break;
                    case OrderType.SetAutoBuildType:
                        {
                            StarSystem system = tok.TokenStarSystem;
                            system.AutoStarShipType = tok.TokenStarShipDescriptor;
                            SubmitStateChange(StateChange.SetAutoBuildType, system, system.AutoStarShipType);
                        }
                        break;
                    case OrderType.ClearOrders:
                        {
                            StarShip ship = tok.TokenStarShip;
                            if (ship == null)
                                continue;
                            ship.Order = null;
                        }
                        break;
                    case OrderType.AttackAdjacent:
                        {
                            StarShip ship = tok.TokenStarShip;
                            if (ship == null)
                                continue;
                            ship.AttackAdjacent = tok.TokenBoolean;
                            SubmitStateChange(StateChange.AttackAdjacent, ship, ship.AttackAdjacent);
                        }
                        break;
                    case OrderType.Move:
                        {
                            StarShip ship = tok.TokenStarShip;
                            if (ship == null)
                                continue;
                            int x = tok.TokenInt;
                            int y = tok.TokenInt;
                            StarShipOrder move = new PointOrder(this, ot, x, y);
                            ship.Order = move;                        
                        }
                        break;
                    case OrderType.BoardCarrier:
                        {
                            StarShip ship = tok.TokenStarShip;
                            if (ship == null)
                                continue;
                            StarShip target = tok.TokenStarShip;
                            if (target == null || target.Destroyed)
                                continue;
                            if (ship == target)
                                continue;
                            MapObjectOrder moo = new MapObjectOrder(this, OrderType.BoardCarrier, target);
                            ship.Order = moo;
                        }
                        break;
                    case OrderType.TargetStarSystem:
                        {
                            StarShip ship = tok.TokenStarShip;
                            if (ship == null)
                                continue;
                            StarSystem system = tok.TokenStarSystem;
                            MapObjectOrder target = new MapObjectOrder(this, OrderType.TargetStarSystem, system);
                            ship.Order = target;
                        }
                        break;
                    case OrderType.TargetStarShip:
                        {
                            StarShip ship = tok.TokenStarShip;
                            if (ship == null)
                                continue;
                            StarShip targetShip = tok.TokenStarShip;
                            if (targetShip == null)
                                continue;
                            MapObjectOrder target = new MapObjectOrder(this, OrderType.TargetStarShip, targetShip);
                            ship.Order = target;
                        }
                        break;
                    case OrderType.TargetMapObject:
                        {
                            StarShip ship = tok.TokenStarShip;
                            if (ship == null)
                                continue;
                            SVMapObject svm = tok.TokenMapObject;
                            if (svm == null)
                                continue;
                            MapObjectOrder moo = new MapObjectOrder(this, OrderType.TargetMapObject, svm);
                            ship.Order = moo;
                        }
                        break;
                    case OrderType.Defend:
                        {
                            StarShip ship = tok.TokenStarShip;
                            if (ship == null)
                                continue;
                            SVMapObject svm = tok.TokenMapObject;
                            MapObjectOrder defend = new MapObjectOrder(this, OrderType.Defend, svm);
                            ship.Order = defend;
                        }
                        break;
                    case OrderType.DefendPoint:
                        {
                            StarShip ship = tok.TokenStarShip;
                            if (ship == null)
                                continue;
                            int x = tok.TokenInt;
                            int y = tok.TokenInt;
                            PointOrder defend = new PointOrder(this, OrderType.DefendPoint, x, y);
                            ship.Order = defend;
                        }
                        break;
                    case OrderType.Escort:
                        {
                            StarShip ship = tok.TokenStarShip;
                            if (ship == null)
                                continue;
                            SVMapObject target = tok.TokenMapObject;
                            if (target == null)
                                continue;
                            int offsetX = tok.TokenInt;
                            int offsetY = tok.TokenInt;
                            EscortOrder escort = new EscortOrder(this, OrderType.Escort, target, offsetX, offsetY);
                            ship.Order = escort;
                        }
                        break;
                    case OrderType.Patrol:
                        {
                            StarShip ship = tok.TokenStarShip;
                            if (ship == null)
                                continue;                            
                            PatrolOrder patrol = new PatrolOrder(this, OrderType.Patrol);
                            int num = tok.TokenInt;
                            while (num > 0)
                            {
                                int x = tok.TokenInt;
                                int y = tok.TokenInt;
                                patrol.PatrolPoints.Add(new SCGPoint(x, y));
                                num--;
                            }
                            ship.Order = patrol;
                        }
                        break;
                    case OrderType.Transport:
                        {
                            StarShip ship = tok.TokenStarShip;
                            if (ship == null)
                                continue;
                            SVMapObject destination = tok.TokenMapObject;
                            if (destination == null)
                                continue;

                            TransportOrder transport = new TransportOrder(this, OrderType.Transport, destination);
                            int sources = tok.TokenInt;
                            while (sources > 0)
                            {
                                transport.Sources.Add(tok.TokenMapObject);
                                sources--;
                            }
                            ship.Order = transport;
                        }
                        break;
                    case OrderType.Unload:
                        {
                            StarShip ship = tok.TokenStarShip;
                            if (ship == null)
                                continue;
                            SVMapObject svm = tok.TokenMapObject;
                            MapObjectOrder moo = new MapObjectOrder(this, OrderType.Unload, svm);
                            ship.Order = moo;
                        }
                        break;
                    case OrderType.TugRelease:
                        {
                            StarShip ship = tok.TokenStarShip;
                            if (ship == null)
                                continue;
                            if (ship is Tug)
                            {
                                Tug tug = ship as Tug;
                                tug.Towing = null;
                            }
                        }
                        break;
                    case OrderType.GravRelease:
                        {
                            StarShip ship = tok.TokenStarShip;
                            if (ship == null)
                                continue;
                            if (ship is GravShip)
                            {
                                GravShip gravShip = ship as GravShip;
                                gravShip.Dragging = null;
                            }
                        }
                        break;
                    case OrderType.CreateGate:
                        {
                            JumpGateShip jumpGate = tok.TokenStarShip as JumpGateShip;
                            if (jumpGate == null)
                                continue;
                            jumpGate.CreateJumpGate();
                        }
                        break;
                    case OrderType.Detonate:
                        {
                            MadBomb mb = tok.TokenStarShip as MadBomb;
                            if (mb == null)
                                continue;
                            mb.Detonate();
                        }
                        break;
                    case OrderType.SetViewPoint:
                        {
                            RemoteViewer rv = tok.TokenStarShip as RemoteViewer;
                            if (rv == null)
                                continue;
                            rv.ViewX = tok.TokenInt;
                            rv.ViewY = tok.TokenInt;
                            SubmitStateChange(StateChange.SetViewPoint, rv, rv.ViewX, rv.ViewY);
                        }
                        break;
                    case OrderType.EjectUDO:
                        {
                            StarShip ship = tok.TokenStarShip;
                            if (ship == null)
                                continue;
                            ship.EjectUDOs();
                            SubmitStateChange(StateChange.EjectUDO, ship);
                        }
                        break;
                    case OrderType.Surrender:
                        {
                            Player p = tok.TokenPlayer;
                            if (!p.Eliminated)
                            {
                                p.Surrendered = true;
                                p.Eliminated = true;                                
                                SubmitStateChange(StateChange.Surrender, p);
                            }
                        }
                        break;
                    case OrderType.Alliance:
                        {
                            Player p1 = tok.TokenPlayer;
                            Player p2 = tok.TokenPlayer;
                            AllianceLevel al = tok.TokenAllianceLevel;
                            p1.SetAlliance(p2, al);
                            SubmitStateChange(StateChange.Alliance, p1, p2, al);
                        }
                        break;
                    case OrderType.Launch:
                        {
                            Carrier carrier = tok.TokenStarShip as Carrier;
                            if (carrier != null)
                                carrier.Launch();
                        }
                        break;
                    default:
                        throw new ArgumentException("Unknown OrderType: " + ot);
                }
            }

            //Process auto orders
            foreach (StarSystem sys in StarSystems)
                if (sys.Owner != null)
                {
                    bool changed = false;

                    //auto-shields
                    if (sys.AutoShields)
                    {
                        while (sys.Resources > sys.Shields)
                        {
                            changed = true;
                            sys.Shields++;
                            sys.Resources -= sys.Shields;
                        }
                    }

                    //auto-scanners
                    if (sys.AutoScanners)
                    {
                        while (sys.Resources > sys.Scanners)
                        {
                            changed = true;
                            sys.Scanners++;
                            sys.Resources -= sys.Scanners;
                        }
                    }

                    //send state change if shields or scanners built
                    if (changed)
                        SubmitStateChange(StateChange.AutoBuildChanges, sys, sys.Shields, sys.Scanners, sys.Resources);

                    //auto-starships
                    if (sys.AutoStarShip)
                    {
                        StarShipDescriptor sd = sys.AutoStarShipType;
                        int buildCost = ShipCostCalculator.GetCost(sd.ShipType, sd.Engines, sd.Value, sd.Cloaked, sys);                  
                        bool built = true;
                        while (sys.Resources > buildCost && built)
                        {
                            built = false;
                            SCGPoint pt = ClosestEmptyPoint(sys.Location);
                            if (sys.IsAdjacent(pt))
                            {
                                built = true;
                                sys.Resources -= buildCost;
                                SubmitStateChange(StateChange.SetResources, sys, sys.Resources);

                                StarShip newShip = StarShip.CreateInstance(this, sys.Owner, sd.ShipType, sd.Engines, sd.Value, sys);
                                newShip.Code = GetShipCode();
                                newShip.SetLocation(pt.X, pt.Y);
                                newShip.Cloaked = sd.Cloaked;

                                //Add state change
                                SubmitStateChange(StateChange.BuildStarShip, newShip.X, newShip.Y, sys.Owner, sd.ShipType, sd.Engines, sd.Value, sd.Cloaked, sys, newShip.Code);

                                //Notify UI
                                if (Host != null)
                                    Host.StarShipCreated(newShip);
                            }

                            //drop out if AutoBuilding one by one
                            if (Scenario.AutoBuildMode == AutoBuildMode.OneByOne)
                                break;
                        }
                    }
                }

            //See if ships within BlackHole range are pulled toward         
            foreach (BlackHole bh in BlackHoles)
            {
                //process starships
                foreach (StarShip ship in StarShips)
                    if (!ship.ProhibitAction)
                        if (bh.PullsInObject(ship, ship.Engines))
                        {
                            //Pull the ship toward the black hole!
                            MoveShipTowardPoint(ship, bh.Location, 20);
                            ship.ProhibitAction = true;
                        }

                //process UDOs pulled toward black holes
                for (int u = UDOs.Count - 1; u >= 0; u--)
                {
                    UDO udo = UDOs[u];
                    if (!udo.IgnoreBlackHoles && udo.Carrier == null)
                        if (bh.PullsInObject(udo, 20))
                        {
                            //UDO pulled toward black hole!
                            int direction = DirectionTowardPoint(udo.Location, bh.Location);
                            int x = udo.X;
                            int y = udo.Y;
                            AdjustPoint(ref x, ref y, direction);
                            MoveUDO(udo, x, y);                                                      

                            //UDO Destroyed!
                            if (udo.Location == bh.Location)
                            {
                                RemoveObject(udo);
                                SubmitStateChange(StateChange.RemoveObject, udo);                
                            }
                        }
                }
            }

            //Set impulse
            impulse = impulse % 20;
            _lineImpulse = impulse % 5;

            //Pre-move processing (Emines, Directors, Tugs)
            foreach (StarShip ship in StarShips)
                ship.PreMoveProcessing();            

            //create a list of StarShips that can act on this Impulse
            List<StarShip> acting = new List<StarShip>();
            foreach (StarShip ship in StarShips)
            {
                //Don't process ships if their action has been canceled by something (tug, paralyship, black hole)
                if (ship.ProhibitAction)
                    continue;

                //Obtain the effective engine rating from the StarShip (GravShips reduce if dragging)
                int engines = ship.EffectiveEngines;           

                //Insert it into the acting list if it can act on this impulse, and not destroyed
                if (engines > 0 && !ship.Destroyed)
                    if (_actions.CanAct(engines, impulse))                     
                        acting.Insert(SVGame.RNG.Next(acting.Count), ship);
            }

            //Go through the list of ships that are acting and carry out their orders
            foreach (StarShip ship in acting)
            {
                //Attack adjacent enemy ships
                if (ship.AttackAdjacent)
                {
                    StarShip enemy = AdjacentEnemyStarShip(ship, ship.X, ship.Y, true, true);
                    if (enemy != null)
                    {
                        ship.TargetStarShip(enemy);
                        continue;
                    }
                }

                //perform special move processing logic (StarTillery, BattleStation)
                if (ship.MoveProcessing())
                    continue;

                //Order type switch
                if (ship.Order != null)
                {
                    switch (ship.Order.OrderType)
                    {
                        case OrderType.Move:
                            {
                                PointOrder move = ship.Order as PointOrder;
                                if (MoveShipTowardPoint(ship, move.TargetPoint))
                                    ship.Order = null;
                            }
                            break;
                        case OrderType.BoardCarrier:
                            {
                                MapObjectOrder moo = ship.Order as MapObjectOrder;
                                Carrier carrier = moo.MapObject as Carrier;
                                if (carrier == null || carrier.IsFull || carrier.Destroyed)
                                    ship.Order = null;
                                if (ship.IsAdjacent(carrier))
                                {
                                    ship.Order = null;
                                    carrier.Embark(ship);
                                }
                                else
                                    MoveShipTowardPoint(ship, carrier.Location);
                            }
                            break;
                        case OrderType.TargetStarSystem:                            
                            {
                                MapObjectOrder target = ship.Order as MapObjectOrder;                          
                                ship.TargetStarSystem(target.StarSystem);
                            }
                            break;
                        case OrderType.TargetStarShip:
                            {
                                MapObjectOrder target = ship.Order as MapObjectOrder;
                                if (TargetValid(target.MapObject, ship.Owner))
                                    ship.TargetStarShip(target.StarShip);
                            }
                            break;
                        case OrderType.TargetMapObject:
                            {
                                MapObjectOrder moo = ship.Order as MapObjectOrder;
                                SVMapObject target = moo.MapObject;
                                if (target is StarSystem || target.VisibleTo[ship.Owner])
                                {
                                    if (ship.IsAdjacent(target))
                                        ship.TargetAdjacentMapObject(target);
                                    else
                                        MoveShipTowardPoint(ship, target.Location);
                                }
                            }
                            break;
                        case OrderType.Defend:
                            {
                                MapObjectOrder defend = ship.Order as MapObjectOrder;
                                SVMapObject svm = defend.MapObject;
                                if (TargetValid(svm, ship.Owner))
                                    ProcessDefendOrder(ship, svm.X, svm.Y);
                            }
                            break;
                        case OrderType.DefendPoint:
                            {
                                PointOrder defend = ship.Order as PointOrder;
                                ProcessDefendOrder(ship, defend.X, defend.Y);
                            }
                            break;
                        case OrderType.Escort:
                            {
                                EscortOrder escort = ship.Order as EscortOrder;
                                SVMapObject target = escort.MapObject;
                                if (TargetValid(target, ship.Owner))
                                {
                                    int x = target.X + escort.OffsetX;
                                    int y = target.Y + escort.OffsetY;
                                    if (ship.X != x || ship.Y != y)
                                        MoveShipTowardPoint(ship, new SCGPoint(x, y));
                                }
                            }
                            break;
                        case OrderType.Patrol:
                            {
                                PatrolOrder patrol = ship.Order as PatrolOrder;
                                int n = patrol.NextPoint;
                                if (MoveShipTowardPoint(ship, patrol.PatrolPoints[n]))
                                {
                                    //reached destination, advance to next patrol point
                                    patrol.Advance();
                                    ship.Order = patrol;                                  
                                }
                            }
                            break;
                        case OrderType.Transport:
                            {
                                TransportOrder transport = ship.Order as TransportOrder;
                                Freighter freighter = ship as Freighter;

                                //head back to destination if cargo bays are full
                                if (freighter.FreeCapacity == 0 && !transport.MovingToDestination)
                                {
                                    transport.MovingToDestination = true;
                                    ship.Order = transport;
                                }

                                //if they have resources, head back to base!
                                if (transport.MovingToDestination)
                                {
                                    //check validity of destination
                                    if (transport.IsSourceValid(transport.MapObject, ship.Owner))
                                    {
                                        if (ship.IsAdjacent(transport.MapObject))
                                        {
                                            if (transport.MapObject is StarSystem)
                                            {
                                                transport.StarSystem.Resources += freighter.Resources;
                                                SubmitStateChange(StateChange.SetResources, transport.StarSystem, transport.StarSystem.Resources);
                                            }
                                            else
                                            {
                                                ResourceStarShip rss = transport.StarShip as ResourceStarShip;
                                                double transportedResources = freighter.Resources;
                                                if (rss is Refinery)
                                                    transportedResources *= 2;
                                                rss.Resources += transportedResources;
                                                SubmitStateChange(StateChange.SetShipResources, rss, rss.Resources);
                                            }
                                            freighter.Resources = 0;
                                            SubmitStateChange(StateChange.SetShipResources, freighter, 0);

                                            //and advance to first source
                                            transport.Advance();                                          
                                            ship.Order = transport;                                         
                                        }
                                        else
                                            MoveShipTowardPoint(ship, transport.MapObject.Location);
                                    }
                                    else
                                    {
                                        //destination not valid, cancel the order
                                        ship.Order = null;
                                    }
                                }
                                else
                                {
                                    //check the validity of the next source
                                    if (transport.IsSourceValid(transport.NextStop, ship.Owner))
                                    {
                                        //are we adjacent to the source?
                                        if (ship.IsAdjacent(transport.NextStop))
                                        {
                                            //pick up resources from the source
                                            int transported = 0;
                                            if (transport.NextStop is StarSystem)
                                            {
                                                StarSystem sysSource = transport.NextStop as StarSystem;
                                                transported = sysSource.ResourcesInt <= freighter.FreeCapacity ? sysSource.ResourcesInt : freighter.FreeCapacity;
                                                sysSource.Resources -= transported;
                                                SubmitStateChange(StateChange.SetResources, sysSource, sysSource.Resources);
                                            }
                                            else
                                            {
                                                ResourceStarShip shipSource = transport.NextStop as ResourceStarShip;
                                                transported = shipSource.ResourcesInt <= freighter.FreeCapacity ? shipSource.ResourcesInt : freighter.FreeCapacity;
                                                shipSource.Resources -= transported;
                                                SubmitStateChange(StateChange.SetShipResources, shipSource, shipSource.Resources);                                              
                                            }
                                            freighter.Resources += transported;
                                            SubmitStateChange(StateChange.SetShipResources, freighter, freighter.Resources);

                                            //Advance the freighter to the next source, or to the destination if its at the last source
                                            transport.Advance();                                          
                                            ship.Order = transport;                                        
                                        }
                                        else
                                            MoveShipTowardPoint(ship, transport.NextStop.Location);
                                    }
                                    else
                                    {
                                        //bypass an invalid source                                        
                                        if (transport.Sources.Count == 0)
                                            ship.Order = null;
                                        else
                                        {
                                            transport.Advance();
                                            ship.Order = transport;
                                        }
                                    }
                                }
                            }
                            break;
                        case OrderType.Unload:
                            {
                                MapObjectOrder moo = ship.Order as MapObjectOrder;                         

                                //validate that we can still drop there
                                bool validDest;
                                if (moo.MapObject is StarSystem)
                                    validDest = moo.StarSystem.Owner == ship.Owner;
                                else
                                    validDest = moo.StarShip.Owner == ship.Owner && !moo.StarShip.Destroyed;

                                //proceed if destination valid
                                if (validDest)
                                {
                                    if (ship.IsAdjacent(moo.MapObject))
                                    {
                                        ResourceStarShip rss = ship as ResourceStarShip;

                                        //we're there, drop off the resources and clear the order
                                        if (moo.MapObject is StarSystem)
                                        {
                                            moo.StarSystem.Resources += rss.Resources;
                                            SubmitStateChange(StateChange.SetResources, moo.StarSystem, moo.StarSystem.Resources);
                                        }
                                        else
                                        {
                                            ResourceStarShip shipDest = moo.StarShip as ResourceStarShip;
                                            shipDest.Resources += rss.Resources;
                                            SubmitStateChange(StateChange.SetShipResources, shipDest, shipDest.Resources);
                                        }

                                        //remove resources from freighter
                                        rss.Resources = 0;
                                        SubmitStateChange(StateChange.SetShipResources, rss, 0);
                                        rss.Order = null;
                                    }
                                    else
                                        MoveShipTowardPoint(ship, moo.MapObject.Location);
                                }
                                else
                                    ship.Order = null;
                            }
                            break;
                        default:
                            throw new ArgumentException("Unsupported Order: " + ship.Order.ToString());
                    }
                }
            }

            //Scenario specific processing
            _commonProcessing = false;
            try
            {
                Scenario.ProcessImpulse(Impulse);

                //Check victory conditions
                if (Scenario.VictoryReached())
                {
                    Victory = true;
                    SubmitStateChange(StateChange.Victory);

                    //ensure formal alliance VC met
                    foreach (Player winner in Players)
                        if (winner.Victor)
                            foreach (Player ally in Players)
                                if (winner.AlliedWith(ally) == AllianceLevel.Formal && ally.AlliedWith(winner) == AllianceLevel.Formal)
                                    ally.Victor = true;

                    //pass victor state changes
                    foreach (Player p in Players)
                        if (p.Victor)
                            SubmitStateChange(StateChange.Victor, p);
                }
            }
            catch
            {
            }

            //common post processing
            CommonPostProcessing();

            //return state change string
            return _tokStateChanges.Result;
        }

        //Process state changes
        public void ProcessStateChanges(string s)
        {
            //common pre-processing
            Impulse++;
            int impulse = Impulse % 20;
            _lineImpulse = impulse % 5;
            CommonPreProcessing();

            //set this flag to true so state changes will not be submitted in common methods
            _commonProcessing = true;

            //parse and process state changes            
            Tokenizer tokMain = new Tokenizer(s);
            while (tokMain.HasTokens)
            {
                string token = tokMain.TokenString;
                SVTokenizer tok = new SVTokenizer(this, token);
                StateChange sc = tok.TokenStateChange;
                switch (sc)
                {
                    case StateChange.SetShields:
                        {
                            StarSystem system = tok.TokenStarSystem;
                            system.Shields = tok.TokenInt;
                        }
                        break;
                    case StateChange.SetScanners:
                        {
                            StarSystem system = tok.TokenStarSystem;
                            system.Scanners = tok.TokenInt;
                        }
                        break;
                    case StateChange.SetResources:
                        {
                            StarSystem system = tok.TokenStarSystem;
                            system.Resources = tok.TokenDouble;
                        }
                        break;
                    case StateChange.BuildStarShip:
                        {
                            //get parameters                            
                            int x = tok.TokenInt;
                            int y = tok.TokenInt;
                            Player player = tok.TokenPlayer;
                            StarShipType st = tok.TokenStarShipType;
                            int engines = tok.TokenInt;
                            int primary = tok.TokenInt;
                            bool cloaked = tok.TokenBoolean;
                            StarSystem system = tok.TokenStarSystem;
                            string code = tok.TokenString;

                            //build the StarShip
                            StarShip ship = StarShip.CreateInstance(this, player, st, engines, primary, system);
                            ship.SetLocation(x, y);
                            ship.Cloaked = cloaked;
                            ship.Code = code;

                            //notify client
                            if (Host != null)
                                Host.StarShipCreated(ship);
                        }
                        break;
                    case StateChange.AutoBuildChanges:
                        {
                            StarSystem sys = tok.TokenStarSystem;
                            sys.Shields = tok.TokenInt;
                            sys.Scanners = tok.TokenInt;
                            sys.Resources = tok.TokenDouble;
                        }
                        break;
                    case StateChange.AssignOrder:
                        {
                            StarShip ship = tok.TokenStarShip;                        
                            ship.Order = tok.TokenStarShipOrder;
                        }
                        break;
                    case StateChange.ClearOrder:
                        {
                            StarShip ship = tok.TokenStarShip;
                            ship.Order = null;
                        }
                        break;
                    case StateChange.AttackAdjacent:
                        {
                            StarShip ship = tok.TokenStarShip;
                            ship.AttackAdjacent = tok.TokenBoolean;
                        }
                        break;
                    case StateChange.AutoShields:
                        {
                            StarSystem system = tok.TokenStarSystem;
                            system.AutoShields = tok.TokenBoolean;
                        }
                        break;
                    case StateChange.AutoScanners:
                        {
                            StarSystem system = tok.TokenStarSystem;
                            system.AutoScanners = tok.TokenBoolean;
                        }
                        break;
                    case StateChange.AutoStarShip:
                        {
                            StarSystem system = tok.TokenStarSystem;
                            system.AutoStarShip = tok.TokenBoolean;
                        }
                        break;
                    case StateChange.ConquerStarSystem:
                        {
                            StarSystem system = tok.TokenStarSystem;
                            Player conqueror = tok.TokenPlayer;
                            ConquerStarSystem(conqueror, system);
                        }
                        break;
                    case StateChange.DestroyStarShip:
                        {
                            StarShip ship = tok.TokenStarShip;
                            DestroyStarShip(ship);
                        }
                        break;
                    case StateChange.AttackStarSystem:
                        {
                            if (Host != null)
                            {
                                StarSystem system = tok.TokenStarSystem;
                                Player attacker = tok.TokenPlayer;
                                Host.StarSystemAttacked(system, attacker);
                            }
                        }
                        break;
                    case StateChange.SetKnownWeapons:
                        {
                            WarShip ship = tok.TokenStarShip as WarShip;                        
                            Player player = tok.TokenPlayer;
                            int knownValue = tok.TokenInt;
                            if (ship.KnownWeapons[player] < knownValue)
                                ship.KnownWeapons[player] = knownValue;
                        }
                        break;
                    case StateChange.Damage:
                        {
                            StarShip ship = tok.TokenStarShip;                         
                            ship.Engines = tok.TokenInt;
                            ship.Value = tok.TokenInt;
                            if (Host != null)
                                Host.StarShipDamaged(ship);
                        }
                        break;
                    case StateChange.SetShipResources:
                        {
                            ResourceStarShip rss = tok.TokenStarShip as ResourceStarShip;
                            rss.Resources = tok.TokenDouble;
                        }
                        break;
                    case StateChange.Victory:
                        Victory = true;
                        break;
                    case StateChange.Victor:
                        tok.TokenPlayer.Victor = true;
                        break;
                    case StateChange.SetAutoBuildType:
                        {
                            StarSystem system = tok.TokenStarSystem;
                            system.AutoStarShipType = tok.TokenStarShipDescriptor;
                        }
                        break;
                    case StateChange.ShipMoved:
                        {
                            StarShip ship = tok.TokenStarShip;                            
                            int x = tok.TokenInt;
                            int y = tok.TokenInt;
                            int direction = tok.TokenInt;
                            int engines = tok.TokenInt;
                            ship.SetLocation(x, y);
                            ship.Facing = direction;
                            if (Host != null)
                                Host.StarShipMoved(ship, direction, engines);
                        }
                        break;
                    case StateChange.ShipHijacked:
                        {
                            StarShip ship = tok.TokenStarShip;
                            Player owner = tok.TokenPlayer;
                            ship.Owner = owner;
                            ship.Order = null;
                            if (Host != null)
                                Host.StarShipOwnerChanged(ship);
                        }
                        break;
                    case StateChange.ScenarioData:
                        {
                            string key = tok.TokenString;
                            string value = tok.TokenString;
                            Scenario.AssignValueFromHost(key, value);
                        }
                        break;
                    case StateChange.DisplayMessage:
                        {
                            if (Host != null)
                            {
                                string msg = tok.TokenString;
                                Host.DisplayMessage(msg);
                            }
                        }
                        break;
                    case StateChange.ObjectRelocated:
                        {
                            SVMapObject svm = tok.TokenMapObject;
                            int x = tok.TokenInt;
                            int y = tok.TokenInt;
                            bool adj = svm.IsAdjacent(new SCGPoint(x, y));
                            svm.SetLocation(x, y);
                            if (Host != null)
                            {
                                if (adj)
                                    Host.ObjectMoved(svm, x, y, 10);
                                else
                                    Host.ObjectRelocated(svm);
                            }
                        }
                        break;
                    case StateChange.Explosion:
                        {
                            if (Host != null)
                            {
                                int x = tok.TokenInt;
                                int y = tok.TokenInt;
                                int particles = tok.TokenInt;
                                SCGColor startColor = tok.TokenColor;
                                SCGColor endColor = tok.TokenColor;
                                int startDiam = tok.TokenInt;
                                int endDiam = tok.TokenInt;
                                int lifeTime = tok.TokenInt;
                                bool playSound = tok.TokenBoolean;
                                Host.CreateExplosion(x, y, particles, startColor, endColor, startDiam, endDiam, lifeTime, playSound);
                            }
                        }
                        break;
                    case StateChange.SetTerrain:
                        {
                            int x = tok.TokenInt;
                            int y = tok.TokenInt;
                            char terrain = tok.TokenChar;
                            SetTerrain(x, y, terrain);
                            if (Host != null)
                                Host.TerrainChanged(x, y, terrain);
                        }
                        break;
                    case StateChange.RemoveObject:
                        {
                            SVMapObject svm = tok.TokenMapObject;
                            RemoveObject(svm);
                        }
                        break;
                    case StateChange.SetShipValues:
                        {
                            StarShip ship = tok.TokenStarShip;
                            ship.Engines = tok.TokenInt;
                            ship.Value = tok.TokenInt;
                        }
                        break;
                    case StateChange.Towing:
                        {
                            Tug tug = tok.TokenStarShip as Tug;
                            tug.Towing = tok.TokenStarShip;
                        }
                        break;
                    case StateChange.Dragging:
                        {
                            GravShip ship = tok.TokenStarShip as GravShip;
                            ship.Dragging = tok.TokenMapObject;
                        }
                        break;
                    case StateChange.AdjustShipValues:
                        {
                            StarShip ship = tok.TokenStarShip;
                            ship.Engines = tok.TokenInt;
                            ship.MaxEngines = tok.TokenInt;
                            ship.Value = tok.TokenInt;
                            ship.MaxValue = tok.TokenInt;
                        }
                        break;
                    case StateChange.AdjustSystemValues:
                        {
                            StarSystem sys = tok.TokenStarSystem;
                            sys.Value = tok.TokenInt;
                            sys.Resources = tok.TokenDouble;
                            sys.Shields = tok.TokenInt;
                            sys.Scanners = tok.TokenInt;
                        }
                        break;
                    case StateChange.ObjectMoved:
                        {
                            SVMapObject svm = tok.TokenMapObject;
                            int x = tok.TokenInt;
                            int y = tok.TokenInt;
                            int engineSpeed = tok.TokenInt;
                            svm.SetLocation(x, y);
                            if (Host != null)
                                Host.ObjectMoved(svm, x, y, engineSpeed);
                        }
                        break;
                    case StateChange.CreateGate:
                        {
                            JumpGateShip jgs = tok.TokenStarShip as JumpGateShip;
                            jgs.CreateJumpGate();
                        }
                        break;
                    case StateChange.BeamAttack:
                        {
                            if (Host != null)
                            {
                                BeamShip ship = tok.TokenStarShip as BeamShip;
                                StarShip target = tok.TokenStarShip;
                                ship.BeamTarget = target;
                                Host.BeamAttack(ship, target);
                            }
                        }
                        break;
                    case StateChange.SetKillPoints:
                        {
                            Elite elite = tok.TokenStarShip as Elite;
                            elite.KillPoints = tok.TokenInt;
                        }
                        break;
                    case StateChange.SetViewPoint:
                        {
                            RemoteViewer rv = tok.TokenStarShip as RemoteViewer;
                            rv.ViewX = tok.TokenInt;
                            rv.ViewY = tok.TokenInt;
                        }
                        break;
                    case StateChange.UDOMoved:
                        {
                            UDO udo = tok.TokenUDO;
                            int x = tok.TokenInt;
                            int y = tok.TokenInt;
                            MoveUDO(udo, x, y);
                        }
                        break;
                    case StateChange.EjectUDO:
                        {
                            StarShip ship = tok.TokenStarShip;
                            ship.EjectUDOs();
                        }
                        break;
                    case StateChange.Surrender:
                        {
                            Player p = tok.TokenPlayer;
                            p.Surrendered = true;
                            p.Eliminated = true;                            
                        }
                        break;
                    case StateChange.Alliance:
                        {
                            Player p1 = tok.TokenPlayer;
                            Player p2 = tok.TokenPlayer;
                            AllianceLevel al = tok.TokenAllianceLevel;
                            p1.SetAlliance(p2, al);
                        }
                        break;
                    case StateChange.CreateStarSystem:
                        {
                            //x, y, value, shields, scanners, resources, sys.Name
                            int x = tok.TokenInt;
                            int y = tok.TokenInt;
                            StarSystem sys = new StarSystem(this);
                            sys.SetLocation(x, y);
                            sys.Value = tok.TokenInt;
                            sys.Shields = tok.TokenInt;
                            sys.Scanners = tok.TokenInt;
                            sys.Resources = tok.TokenDouble;
                            sys.Name = tok.TokenString;
                            if (Host != null)
                                Host.StarSystemCreated(sys);
                        }
                        break;
                    case StateChange.CreateWormhole:
                        {
                            Wormhole wh = new Wormhole(this);
                            int x = tok.TokenInt;
                            int y = tok.TokenInt;
                            wh.SetLocation(x, y);
                            wh.DestX = tok.TokenInt;
                            wh.DestY = tok.TokenInt;
                            if (Host != null)
                                Host.MapObjectCreated(wh);
                        }
                        break;
                    case StateChange.CreateBlackHole:
                        {
                            BlackHole bh = new BlackHole(this);
                            int x = tok.TokenInt;
                            int y = tok.TokenInt;
                            bh.SetLocation(x, y);
                            bh.Radius = tok.TokenInt;
                            if (Host != null)
                                Host.MapObjectCreated(bh);
                        }
                        break;
                    case StateChange.CreatePulsar:
                        {
                            Pulsar p = new Pulsar(this);
                            int x = tok.TokenInt;
                            int y = tok.TokenInt;
                            p.SetLocation(x, y);
                            if (Host != null)
                                Host.MapObjectCreated(p);
                        }
                        break;
                    case StateChange.SetBlackHoleRadius:
                        {
                            BlackHole bh = tok.TokenMapObject as BlackHole;
                            bh.Radius = tok.TokenInt;
                        }
                        break;
                    case StateChange.CreateUDO:
                        {                            
                            UDO udo = new UDO(this);
                            int x = tok.TokenInt;
                            int y = tok.TokenInt;
                            udo.SetLocation(x, y);
                            udo.Name = tok.TokenString;
                            udo.ImageFileName = tok.TokenString;
                            udo.Description = tok.TokenString;
                            udo.AutoPickUp = tok.TokenBoolean;
                            udo.IgnoreBlackHoles = tok.TokenBoolean;
                            udo.IsTaggable = tok.TokenBoolean;
                            if (Host != null)
                                Host.UDOCreated(udo);
                        }
                        break;
                    case StateChange.EmbarkStarShip:
                        {
                            StarShip ship = tok.TokenStarShip;
                            Carrier carrier = tok.TokenStarShip as Carrier;
                            carrier.Embark(ship);
                        }
                        break;
                    case StateChange.LaunchShips:
                        {
                            Carrier carrier = tok.TokenStarShip as Carrier;
                            carrier.Launch();
                        }
                        break;
                    case StateChange.ConvertStarSystem:
                        {
                            StarSystem sys = tok.TokenStarSystem;
                            Player player = tok.TokenPlayer;
                            if (Host != null)
                                Host.StarSystemConverted(sys, player);
                        }
                        break;
                    default:
                        throw new ArgumentException("Unknown StateChange: " + sc);
                }
            }

            //common post processing
            CommonPostProcessing();
        }

        //Return a list of valid points adjacent to a point
        public List<SCGPoint> AdjacentPoints(SCGPoint pt, bool mustBeEmpty)
        {
            List<SCGPoint> lst = new List<SCGPoint>();
            for (int x = -1; x <= 1; x++)
                for (int y = -1; y <= 1; y++)
                    if (x != 0 || y != 0)
                    {
                        int px = (int)(pt.X + x);
                        int py = (int)(pt.Y + y);
                        if (px >= 0 && px < Width && py >= 0 && py < Height)
                            if (!mustBeEmpty || GetObjectAt(px, py) == null || GetObjectAt(px, py) is UDO)
                                lst.Add(new SCGPoint(px, py));
                    }
            return lst;
        }

        //Find the closest empty point to the point specified (inclusive)
        public SCGPoint ClosestEmptyPoint(SCGPoint pt)
        {
            int x = (int)pt.X;
            int y = (int)pt.Y;
            if (InBounds(x, y) && _map[x, y] == null)
                return pt;
            int counter = 0;
            int limit = 1;
            int direction = 0;
            do
            {
                AdjustPoint(ref x, ref y, direction);
                counter++;
                if (counter == limit)
                {
                    counter = 0;
                    direction += 2;
                    if (direction == 4)
                        limit++;
                    else if (direction == 8)
                    {
                        limit++;
                        direction = 0;
                    }
                }
            }
            while (!InBounds(x, y) || _map[x, y] != null);
            return new SCGPoint(x, y);
        }

        //Set visibility of objects
        public void SetVisibility()
        {
            //initialize visibility to false
            foreach (Player player in Players)
            {
                foreach (StarSystem sys in StarSystems)
                    sys.VisTemp[player] = sys.Owner == player;
                foreach (StarShip ship in StarShips)
                    ship.VisTemp[player] = ship.Owner == player;
                foreach (Wormhole wh in Wormholes)
                    wh.VisTemp[player] = wh.DiscoveredBy[player];
                foreach (BlackHole bh in BlackHoles)
                    bh.VisTemp[player] = bh.DiscoveredBy[player];
                foreach (Pulsar ps in Pulsars)
                    ps.VisibleTo[player] = ps.DiscoveredBy[player];
                foreach (UDO udo in UDOs)
                    udo.VisTemp[player] = udo.TaggedBy[player];
            }

            //visiblity to StarSystems
            foreach (StarSystem system in StarSystems)
                if (system.Owner != null)
                    SetVisibility(system.Location, system.Owner, system.Scanners, false, false);

            //visibility to StarShips
            foreach (StarShip ship in StarShips)
            {
                SetVisibility(ship.Location, ship.Owner, ship.Scanners, ship.CanSpy, false);

                //Some StarShips (RemoteViewers) have special VisibilityProcessing
                ship.VisibilityProcessing();           
            }

            //StarShips that are tagged also remain visible
            foreach (Player p in Players)
                foreach (StarShip ship in StarShips)
                    if (ship.TaggedBy[p])
                        ship.VisTemp[p] = true;

            //Pass visiblity along to allies            
            foreach (Player p in Players)
                foreach (Player ally in Players)
                    if (p.AlliedWith(ally) >= AllianceLevel.Visibility)
                        foreach (SVMapObject svm in MapObjects)
                        {
                            if (svm.VisTemp[p])
                                if (svm.PassesVisibilityToAllies)
                                    svm.VisTemp[ally] = true;
                            if (svm.DiscoveredBy[p])
                                if (svm.PassesVisibilityToAllies)
                                    svm.DiscoveredBy[ally] = true;
                        }             

            //Assign temp values to main values
            foreach (Player p in Players)
                foreach (SVMapObject svm in MapObjects)
                    svm.VisibleTo[p] = svm.VisTemp[p];
        }
        internal void SetVisibility(SCGPoint pt, Player player, int range, bool spy, bool ignoreNebula)
        {
            foreach (SVMapObject target in MapObjects)
            {
                if (!target.VisTemp[player] || spy)
                {
                    bool vis = false;
                    if (target.IsAdjacent(pt))
                        vis = true;
                    else if (_terrainMap[(int)pt.X, (int)pt.Y] == ' ' && _terrainMap[target.X, target.Y] == ' ' &&
                        target.Distance((int)pt.X, (int)pt.Y) <= range && !(target is BlackHole))
                    {
                        vis = true;

                        //handle cloaked StarShips
                        if (target is StarShip)
                        {
                            StarShip ship = target as StarShip;
                            if (ship.Cloaked)
                                vis = false;
                        }
                    }
                    else if (ignoreNebula)
                    {
                        if (target.Distance((int)pt.X, (int)pt.Y) <= range && !(target is BlackHole))
                        {
                            vis = true;

                            //handle cloaked StarShips for RV's too
                            if (target is StarShip)
                            {
                                StarShip ship = target as StarShip;
                                if (ship.Cloaked)
                                    vis = false;
                            }
                        }
                    }
                    if (vis)
                    {
                        target.VisTemp[player] = vis;

                        //SpyShips identify enemies
                        if (spy)
                            if (target is StarShip)
                                target.DiscoveredBy[player] = true;
                    }

                    //Wormholes, BlackHoles, Pulsars remain visible once discovered
                    if (target is Wormhole || target is BlackHole || target is Pulsar)
                        if (vis)
                            target.DiscoveredBy[player] = true;
                }
            }
        }

        //Determine the direction needed to travel to specified point
        public int DirectionTowardPoint(SCGPoint pt1, SCGPoint pt2)
        {
            int xdiff = (int)(pt2.X - pt1.X);
            int ydiff = (int)(pt2.Y - pt1.Y);
            if (xdiff == 0 && ydiff == 0)
                return -1;

            //if adjacent use simpler calculation
            if (Math.Abs(xdiff) <= 1 && Math.Abs(ydiff) <= 1)
            {
                if (xdiff == 1)
                {
                    if (ydiff == 0)
                        return 2;
                    else if (ydiff == 1)
                        return 3;
                    else
                        return 1;
                }
                else if (xdiff == -1)
                {
                    if (ydiff == 0)
                        return 6;
                    else if (ydiff == 1)
                        return 5;
                    else
                        return 7;
                }
                else
                {
                    if (ydiff == 1)
                        return 4;
                    else
                        return 0;
                }
            }

            int direction;
            if (xdiff == 0)
            {
                direction = ydiff > 0 ? 4 : 0;
            }
            else if (xdiff > 0)
            {
                direction = ydiff == 0 ? 2 : ydiff > 0 ? 3 : 1;
            }
            else
            {
                direction = ydiff == 0 ? 6 : ydiff > 0 ? 5 : 7;
            }

            xdiff = Math.Abs(xdiff);
            ydiff = Math.Abs(ydiff);

            switch (direction)
            {
                case 1:
                    if (xdiff > ydiff)
                        AdjustDirection(xdiff, ydiff, 2, ref direction);
                    else
                        AdjustDirection(ydiff, xdiff, 0, ref direction);
                    break;
                case 3:
                    if (xdiff > ydiff)
                        AdjustDirection(xdiff, ydiff, 2, ref direction);
                    else
                        AdjustDirection(ydiff, xdiff, 4, ref direction);
                    break;
                case 5:
                    if (xdiff > ydiff)
                        AdjustDirection(xdiff, ydiff, 6, ref direction);
                    else
                        AdjustDirection(ydiff, xdiff, 4, ref direction);
                    break;
                case 7:
                    if (xdiff > ydiff)
                        AdjustDirection(xdiff, ydiff, 6, ref direction);
                    else
                        AdjustDirection(ydiff, xdiff, 0, ref direction);
                    break;
            }
            return direction;
        }

        //Persistence
        public string Persist()
        {
            SVTokenizer tok = new SVTokenizer(this);

            //versioning
            tok.AppendToken(FILE_VERSION);

            //scenario name        
            tok.AppendToken(Scenario.GetType().Name);
            tok.AppendToken(Scenario.GetDataString());

            //misc stuff
            tok.AppendToken(Impulse);

            //map dimensions
            tok.Tokenize(Width, Height);

            //terrain map
            string terrainEncoded = "";
            char lastTerrain = '?';
            int runCount = 0;
            for (int y = 0; y < Height; y++)
                for (int x = 0; x < Width; x++)
                    if (_terrainMap[x, y] != lastTerrain)
                    {
                        if (runCount > 0)
                            terrainEncoded += lastTerrain.ToString() + runCount + "|";
                        lastTerrain = _terrainMap[x, y];
                        runCount = 1;
                    }
                    else
                        runCount++;
            terrainEncoded += lastTerrain.ToString() + runCount + "|";
            tok.AppendToken(terrainEncoded);
            
            //Players
            tok.AppendToken(Players.Count);
            foreach (Player p in Players)
                tok.AppendToken(p.Persist());

            //StarSystems
            tok.AppendToken(StarSystems.Count);
            foreach (StarSystem system in StarSystems)
                tok.AppendToken(system.Persist());

            //Wormholes
            tok.AppendToken(Wormholes.Count);
            foreach (Wormhole wh in Wormholes)
                tok.AppendToken(wh.Persist());

            //Pulsars
            tok.AppendToken(Pulsars.Count);
            foreach (Pulsar p in Pulsars)
                tok.AppendToken(p.Persist());

            //BlackHoles
            tok.AppendToken(BlackHoles.Count);
            foreach (BlackHole bh in BlackHoles)
                tok.AppendToken(bh.Persist());

            //StarShips
            tok.AppendToken(StarShips.Count);
            foreach (StarShip ship in StarShips)
                tok.AppendToken(ship.Persist());

            //UDOs
            tok.AppendToken(UDOs.Count);
            foreach (UDO udo in UDOs)
                tok.AppendToken(udo.Persist());

            //Terrain images
            tok.AppendToken(ScenarioImages.Count);
            foreach (ScenarioImage si in ScenarioImages)
                tok.AppendToken(si.Persist());

            return tok.Result;
        }
        public void Parse(string s, ISVGameReconstituter reconstituter)
        {
            SVTokenizer tok = new SVTokenizer(this, s);

            //Versioning
            int version = tok.TokenInt;     

            //Scenario
            string scenarioName = tok.TokenString;
            Scenario = reconstituter.GetScenario(scenarioName);          
            Scenario.Game = this;
            Scenario.SetDataString(tok.TokenString);
            Scenario.ClientInitialize();

            //Misc stuff
            Impulse = tok.TokenInt;

            //map dimensions
            int width = tok.TokenInt;
            int height = tok.TokenInt;
            SetDimensions(width, height);

            //terrain grid
            string terrainEncoded = tok.TokenString;
            string[] terrainCells = terrainEncoded.Split('|');
            int x = 0;
            int y = 0;
            foreach (string terrainCell in terrainCells)
            {
                if (terrainCell == "")
                    break;
                char terrain = terrainCell[0];
                int runCount = Int32.Parse(terrainCell.Substring(1));
                while (runCount > 0)
                {
                    _terrainMap[x, y] = terrain;
                    x++;
                    if (x == Width)
                    {
                        x = 0;
                        y++;
                    }
                    runCount--;
                }
            }

            //Players
            int n = tok.TokenInt;
            while (n > 0)
            {
                Player p = new Player(this);
                p.Parse(tok.TokenString, reconstituter);
                n--;
            }

            //StarSystems
            n = tok.TokenInt;
            while (n > 0)
            {
                StarSystem system = new StarSystem(this);
                system.Parse(tok.TokenString);
                PlaceInMasterList(system);
                n--;
            }   
   
            //Wormholes
            n = tok.TokenInt;
            while (n > 0)
            {
                Wormhole wh = new Wormhole(this);
                wh.Parse(tok.TokenString);
                PlaceInMasterList(wh);
                n--;
            }

            //Pulsars
            n = tok.TokenInt;
            while (n > 0)
            {
                Pulsar p = new Pulsar(this);
                p.Parse(tok.TokenString);
                PlaceInMasterList(p);
                n--;
            }

            //BlackHoles
            n = tok.TokenInt;
            while (n > 0)
            {
                BlackHole bh = new BlackHole(this);
                bh.Parse(tok.TokenString);
                PlaceInMasterList(bh);
                n--;
            }

            //StarShips
            n = tok.TokenInt;
            while (n > 0)
            {
                StarShip ship = StarShip.CreateInstance(this, tok.TokenString);
                PlaceInMasterList(ship);
                n--;
            }
            foreach (StarShip ship in StarShips)
                ship.Relink();

            //UDOs
            n = tok.TokenInt;
            while (n > 0)
            {
                UDO udo = new UDO(this);
                udo.Parse(tok.TokenString);
                PlaceInMasterList(udo);
                n--;
            }

            //Terrain images
            n = tok.TokenInt;
            while (n > 0)
            {
                ScenarioImage si = new ScenarioImage(this);
                si.Parse(tok.TokenString);
                n--;
            }

            //remove incorrect references in masterlist
            for (int m = 0; m < _masterList.Count; m++)
            {
                SVMapObject svm = _masterList[m];
                if (svm != null)
                    if (svm.ID != m)
                        _masterList[m] = null;
            }

            //remove trailing nulls in the masterlist
            while (_masterList.Count > 0 && _masterList[_masterList.Count - 1] == null)
                _masterList.RemoveAt(_masterList.Count - 1);

            //Relinking
            foreach (Player p in Players)
                p.Relink();
        }

        //private members       
        private const int FILE_VERSION = 3;
        private int _width = 0;
        private int _height = 0;
        private List<SVMapObject> _mapObjects = new List<SVMapObject>();
        private List<SVMapObject> _masterList = new List<SVMapObject>();
        private List<Player> _players = new List<Player>();
        private List<StarSystem> _starSystems = new List<StarSystem>();
        private List<StarShip> _starShips = new List<StarShip>();
        private SVMapObject[,] _map = null;
        private char[,] _terrainMap = null;
        private int _lineImpulse = 0;
        internal List<string> _shipCodes = new List<string>();
        private int _shipCodeLength = 2;
        private int _shipCodeIncrease = (int)(Math.Pow(36, 2) * 0.7);
        private List<Wormhole> _wormholes = new List<Wormhole>();
        private List<BlackHole> _blackHoles = new List<BlackHole>();
        private List<Pulsar> _pulsars = new List<Pulsar>();
        private List<UDO> _udos = new List<UDO>();
        private Tokenizer _tokStateChanges;
        private ImpulseActions _actions = new ImpulseActions(20);
        private bool _commonProcessing = false;
        private Player _currentPlayer = null;
        private List<ScenarioImage> _scenarioImages = new List<ScenarioImage>();
        private Scenario _scenario;

        //Access ImpulseActions instance
        internal ImpulseActions ImpulseActions
        {
            get
            {
                return _actions;
            }
        }

        //Register a new MapObject - and give it its unique, lifetime, ID
        internal void RegisterMapObject(SVMapObject svm)
        {
            _mapObjects.Add(svm);
            _masterList.Add(svm);
            svm.ID = _masterList.Count - 1;
        }

        //Make sure item has correct place in master list
        private void PlaceInMasterList(SVMapObject svm)
        {
            while (_masterList.Count < svm.ID + 1)
                _masterList.Add(null);
            _masterList[svm.ID] = svm;
        }

        //De-register a MapObject - null it's place in the master list
        internal void DeRegisterMapObject(SVMapObject svm)
        {
            _mapObjects.Remove(svm);
            _masterList[svm.ID] = null;
        }

        //Set the dimensions of the map
        internal void SetDimensions(int width, int height)
        {
            _width = width;
            _height = height;
            _terrainMap = new char[Width, Height];
            _map = new SVMapObject[Width, Height];

            //and clear terrain
            for (int w = 0; w < width; w++)
                for (int h = 0; h < height; h++)
                    _terrainMap[w, h] = ' ';
        }

        //Adjust the point to the specified direction
        internal void AdjustPoint(ref int x, ref int y, int direction)
        {
            switch (direction)
            {
                case 0:
                    y--;
                    break;
                case 1:
                    y--;
                    x++;
                    break;
                case 2:
                    x++;
                    break;
                case 3:
                    x++;
                    y++;
                    break;
                case 4:
                    y++;
                    break;
                case 5:
                    y++;
                    x--;
                    break;
                case 6:
                    x--;
                    break;
                case 7:
                    x--;
                    y--;
                    break;
            }
        }

        //Make the coordinates point to the closest empty map space
        internal void SetToClosestEmpty(ref int x, ref int y)
        {
            if (GetObjectAt(x, y) == null && InBounds(x, y))
                return;
            int counter = 0;
            int limit = 1;
            int direction = 0;
            do
            {
                AdjustPoint(ref x, ref y, direction);
                counter++;
                if (counter == limit)
                {
                    counter = 0;
                    direction += 2;
                    if (direction == 4)
                        limit++;
                    else if (direction == 8)
                    {
                        limit++;
                        direction = 0;
                    }
                }
            }
            while (GetObjectAt(x, y) != null || !InBounds(x, y));
        }

        //Return a new random ship code
        internal string GetShipCode()
        {
            //increase length of codes?
            if (_shipCodes.Count > _shipCodeIncrease)
            {
                _shipCodeLength++;
                _shipCodeIncrease = (int)(Math.Pow(36, _shipCodeLength) * 0.7);
                _shipCodes.Clear();
            }

            string s = "";
            do
            {
                s = "";
                for (int i = 0; i < _shipCodeLength; i++)
                {
                    char c;
                    int n = RNG.Next(36);
                    if (n >= 26)
                        c = (char)((int)'0' + (n - 26));
                    else
                        c = (char)((int)'A' + n);
                    s += c;
                }
            }
            while (_shipCodes.Contains(s));
            _shipCodes.Add(s);
            return s;
        }

        //Adjust direction to keep lines straighter
        private void AdjustDirection(int term1, int term2, int newDir, ref int direction)
        {
            int d = term1 / term2;
            if (d > _lineImpulse)
                direction = newDir;
        }

        //Common pre-processing
        private void CommonPreProcessing()
        {
            _commonProcessing = true;

            //produce resources and clear flags
            foreach (StarSystem sys in StarSystems)
            {
                sys.ProduceResources();
                sys.ClearInfluence();
            }

            //up front initialization
            foreach (StarShip ship in StarShips)
            {              
                ship.ProhibitAction = false;
                ship.ProhibitMove = false;
                ship.Initialize();
            }

            //StarShip common processing
            foreach (StarShip ship in StarShips)
            {
                ship.CommonPreProcessing();              

                //StarShips standing over wormholes jump
                foreach (Wormhole wh in Wormholes)
                    if (ship.Location == wh.Location)
                    {
                        SCGPoint dest = ClosestEmptyPoint(wh.Destination);
                        ship.SetLocation(dest.X, dest.Y);
                        ship.Order = null;
                        _map[wh.X, wh.Y] = wh;
                        wh.TraveledBy[ship.Owner] = true;
                        if (Host != null)
                            Host.WormholeTraveled(ship);

                        //Perform special logic (tugs)
                        ship.WormholeTraveled();
                    }

                //StarShips standing over BlackHoles are destroyed
                foreach (BlackHole bh in BlackHoles)
                    if (ship.Location == bh.Location)
                    {
                        DestroyStarShip(ship);
                        _map[bh.X, bh.Y] = bh;
                    }

                //StarShips next to Pulsars are completely repaired
                foreach (Pulsar ps in Pulsars)
                    if (ship.IsAdjacent(ps.Location))
                    {
                        ship.Engines = ship.MaxEngines;
                        ship.Value = ship.MaxValue;
                    }
            
            }

            _commonProcessing = false;
        }

        //Common post-processing
        private void CommonPostProcessing()
        {
            _commonProcessing = true;

            //Scenario processing
            Scenario.ClientProcessImpulse(Impulse);

            //PostProcessing for StarShips
            foreach (StarShip ship in StarShips)
                ship.CommonPostProcessing();

            //Move UDOs back onto the board
            foreach (UDO udo in UDOs)
                if (InBounds(udo.X, udo.Y))
                    if (GetObjectAt(udo.X, udo.Y) == null)
                        _map[udo.X, udo.Y] = udo;

            //Remove destroyed StarShips
            RemoveDestroyedStarShips();

            //place black holes back on map
            foreach (BlackHole bh in BlackHoles)
                _map[bh.X, bh.Y] = bh;

            //visibility
            SetVisibility();

            _commonProcessing = false;
        }

        //submit an order
        //public void SubmitOrder(

        //submit a state change
        internal void SubmitStateChange(StateChange sc, params object[] parameters)
        {
            if (_commonProcessing || _tokStateChanges == null)
                return;

            //tokenize the state change
            SVTokenizer tok = new SVTokenizer(this);
            tok.AppendToken(sc);
            foreach (object obj in parameters)
                tok.AppendToken(obj);

            //add to running list
            _tokStateChanges.AppendToken(tok.Result);
        }

        //Move a StarShip toward a point
        internal bool MoveShipTowardPoint(StarShip ship, SCGPoint pt)
        {
            return MoveShipTowardPoint(ship, pt, 0);
        }
        internal bool MoveShipTowardPoint(StarShip ship, SCGPoint pt, int engineSpeed)
        {
            //if already at the point no action needed
            if (ship.Location == pt)
                return true;

            //Destroyed?
            if (ship.Destroyed)
                return false;

            //Move prohibited?
            if (ship.ProhibitMove)
                if (engineSpeed == 0)
                    return false;       

            bool canMove = true;
            int direction = DirectionTowardPoint(ship.Location, pt);
            if (!CanMoveDirection(ship, direction))
            {
                ChangeDirection(ref direction, 1);
                if (!CanMoveDirection(ship, direction))
                {
                    ChangeDirection(ref direction, -2);
                    if (!CanMoveDirection(ship, direction))
                    {
                        ChangeDirection(ref direction, 3);
                        if (!CanMoveDirection(ship, direction))
                        {
                            ChangeDirection(ref direction, -4);
                            if (!CanMoveDirection(ship, direction))
                                canMove = false;
                        }
                    }
                }
            }

            if (canMove)
            {
                int oldx = ship.X;
                int oldy = ship.Y;
                int shipx = ship.X;
                int shipy = ship.Y;
                AdjustPoint(ref shipx, ref shipy, direction);
                ship.SetLocation(shipx, shipy);
                ship.Facing = direction;

                int engines = engineSpeed == 0 ? ship.EffectiveEngines : engineSpeed;

                //Notify host
                if (Host != null)           
                    Host.StarShipMoved(ship, direction, engines);                

                //Notify clients that a ship was moved
                SubmitStateChange(StateChange.ShipMoved, ship, ship.X, ship.Y, direction, engines);

                //Perform ship specific logic
                ship.PostMove(oldx, oldy);
            }

            return (ship.X == pt.X && ship.Y == pt.Y);
        }

        //Perform a repair on a StarShip
        internal void RepairStarShip(StarShip ship)
        {
            int engineDamage = ship.MaxEngines - ship.Engines;
            int extraDamage = ship.MaxValue - ship.Value;
            int num = SVGame.RNG.Next(engineDamage + extraDamage);
            if (num < engineDamage)
                ship.Engines++;
            else
                ship.Value++;
            SubmitStateChange(StateChange.SetShipValues, ship, ship.Engines, ship.Value);
        }

        //Can a StarShip move in the specified direction?
        private bool CanMoveDirection(StarShip ship, int direction)
        {
            int x = ship.X;
            int y = ship.Y;
            AdjustPoint(ref x, ref y, direction);
            return CanMoveTo(ship, x, y);
        }

        //Adjust direction by a specified amount
        private void ChangeDirection(ref int direction, int amount)
        {
            direction = direction + amount;
            if (direction < 0)
                direction += 8;
            if (direction >= 8)
                direction -= 8;
        }

        //Can a StarShip move to the specified point?
        private bool CanMoveTo(StarShip ship, int x, int y)
        {
            //check bounds
            if (!InBounds(x, y))
                return false;

            //can't move onto certain objects       
            SVMapObject svm = GetObjectAt(x, y);
            if (svm is StarSystem)
                return false;
            if (svm is StarShip)
                return (svm as StarShip).Destroyed;
            if (svm is Pulsar)
                return false;

            //Some ships have special move logic
            return ship.CanMoveTo(x, y);
        }

        //Safely move a UDO
        internal void MoveUDO(UDO udo, int x, int y)
        {
            bool adjacentMove = udo.IsAdjacent(new SCGPoint(x, y));

            //Relocate safely
            udo.SetLocationSafely(x, y);

            //Notify host UDO moved
            if (Host != null)
            {
                if (adjacentMove)
                {
                    int engine = 10;
                    if (udo.Carrier != null)
                        engine = udo.Carrier.EffectiveEngines;
                    Host.ObjectMoved(udo, x, y, engine);
                }
                else
                    Host.ObjectRelocated(udo);
            }

            //notify clients
            SubmitStateChange(StateChange.UDOMoved, udo, x, y);
        }

        //Remove an object
        private void RemoveObject(SVMapObject svm)
        {           
            if (svm is StarSystem)
            {
                //remove capital references
                StarSystem target = svm as StarSystem;                
                if (target.IsCapital && target.Owner != null)
                    target.Owner.Capital = null;
                target.Remove();
            }
            else if (svm is StarShip)
            {
                StarShip ship = svm as StarShip;
                ship.Order = null;
                ship.Destroyed = true;
            }
            else
            {
                //remove from game
                svm.Remove();
            }

            //clear any orders that are referencing this object if its visible to the starship's owner
            foreach(StarShip ship in StarShips)
                if (ship.Order != null && ship.Order is MapObjectOrder)
                {
                    MapObjectOrder moo = ship.Order as MapObjectOrder;
                    if (moo.MapObject == svm)
                        if ((svm is StarSystem) || (svm.VisibleTo[ship.Owner]))
                            ship.Order = null;
                }

            //alert client
            if (Host != null)
                Host.ObjectRemoved(svm);
        }

        //Destroy a StarShip
        internal void DestroyStarShip(StarShip ship)
        {
            if (Host != null)
                Host.StarShipDestroyed(ship);
            ship.Destroyed = true;

            //submit state change
            SubmitStateChange(StateChange.DestroyStarShip, ship);                     
        }

        //Conquer a StarSystem
        internal void ConquerStarSystem(Player player, StarSystem system)
        {
            if (system.Owner == player)
                return;

            //Conquer a player's capital?
            if (system.Owner != null && system.IsCapital)
                ConquerCapital(player, system);

            //Take it over!
            system.Owner = player;
            system.Shields = 0;
            system.AutoShields = false;
            system.AutoScanners = false;
            system.AutoStarShip = false;

            //Notify the host
            if (Host != null)
                Host.StarSystemConquered(system);

            //dont let them autobuild ships they cant build
            if (!player.ShipTypesAvailable.Contains(system.AutoStarShipType.ShipType))
                system.AutoStarShipType = new StarShipDescriptor(StarShipType.WarShip, 8, 4, false);

            //submit the state change
            SubmitStateChange(StateChange.ConquerStarSystem, system, player);          
        }

        //Conquer a capital
        private void ConquerCapital(Player player, StarSystem system)
        {
            //Avoid the capital conquering logic?
            if (!Scenario.CapitalElimination)
                return;

            Player conqueredPlayer = system.Owner;
            conqueredPlayer.Eliminated = true;

            //take their StarSystems
            foreach (StarSystem sys in StarSystems)
                if (sys.Owner == conqueredPlayer)
                    sys.Owner = player;

            //take their StarShips
            foreach (StarShip ship in StarShips)
                if (ship.Owner == conqueredPlayer)
                {
                    ship.Owner = player;                   
                    ship.Order = null;
                    if (Host != null)
                        Host.StarShipOwnerChanged(ship);
                }

            //take their ship types
            foreach (StarShipType st in conqueredPlayer.ShipTypesAvailable)
                if (!player.ShipTypesAvailable.Contains(st))
                {
                    player.ShipTypesAvailable.Add(st);
                    player.ShipTypesAvailable.Sort();
                }

            //unassign their capital
            conqueredPlayer.Capital = null;
        }

        //Remove destroyed starships, notify client, remove orders that have this one as a target
        private void RemoveDestroyedStarShips()
        {
            lock(StarShips)
            {
                for (int i = StarShips.Count - 1; i >= 0; i--)
                {
                    StarShip ship = StarShips[i];
                    if (ship.Destroyed)
                    {
                        //remove the ship, and notify host
                        ship.Remove();
                    }
                }
            }

            //Make sure starships that moved into destroyed zones are represented on logical map
            foreach (StarShip ship in StarShips)
                SetObjectAt(ship.X, ship.Y, ship);
        }

        //Return an enemy StarShip that is adjacent to the specified point
        internal StarShip AdjacentEnemyStarShip(StarShip ship, int x, int y, bool onlyIncludePossibleTargets, bool considerAlliance)
        {
            IList<SCGPoint> adj = AdjacentPoints(new SCGPoint(x, y), false);
            foreach (SCGPoint ptAdj in adj)
            {
                SVMapObject svm = _map[(int)ptAdj.X, (int)ptAdj.Y];
                if (svm is StarShip)
                {
                    Player p = ship.Owner;
                    StarShip enemy = svm as StarShip;
                    if (enemy.VisibleTo[p] && !enemy.Destroyed)
                    {
                        if (enemy.Owner == p)
                            continue;                    
                        if (onlyIncludePossibleTargets && !ship.CanDestroy(enemy))
                            continue;
                        if (!considerAlliance)
                            return enemy;
                        if (!ship.Owner.FriendlyTo(enemy.Owner))
                            return enemy;
                    }
                }
            }
            return null;
        }

        //Process defend logic
        private void ProcessDefendOrder(StarShip ship, int defendX, int defendY)
        {                 
            if (ship.Distance(defendX, defendY) > 7)
                MoveShipTowardPoint(ship, new SCGPoint(defendX, defendY));
            else
            {
                //Find closest enemy StarShip within closing range
                StarShip closestEnemy = null;
                double dist = Double.MaxValue;
                foreach (Player p in Players)
                    if (p != ship.Owner && ship.Owner.AlliedWith(p) == AllianceLevel.None)
                        foreach (StarShip shipCheck in p.StarShips)                        
                            if (shipCheck.VisibleTo[ship.Owner] && ship.CanDestroy(shipCheck))
                            {
                                double distCheck = shipCheck.Distance(defendX, defendY);
                                if (distCheck <= 10)
                                    if (distCheck < dist)
                                    {
                                        closestEnemy = shipCheck;
                                        dist = distCheck;
                                    }
                            }

                //if there is an enemy in range, attack it
                if (closestEnemy != null)
                {
                    if (ship.IsAdjacent(closestEnemy))
                    {
                        //Attacking a starship
                        ship.TargetStarShip(closestEnemy);
                    }
                    else
                        MoveShipTowardPoint(ship, closestEnemy.Location);
                }
            }
        }

        //Build a StarShip
        private StarShip BuildStarShip(StarSystem system, Player owner, int x, int y, StarShipType st, int engines, int primary, bool cloaked)
        {
            //validate build starship location
            if (!InBounds(x, y))
                return null;

            if (GetObjectAt(x, y) != null && !(GetObjectAt(x, y) is UDO))
                return null;

            //Deduct resources for ship cost
            int cost = ShipCostCalculator.GetCost(st, engines, primary, cloaked, system);
            if (system != null)
            {
                if (cost > system.ResourcesInt)
                    return null;
                system.Resources -= cost;
                SubmitStateChange(StateChange.SetResources, system, system.Resources);
            }

            //create the StarShip instance
            StarShip ship = StarShip.CreateInstance(this, owner, st, engines, primary, system);
            ship.Cloaked = cloaked;
            ship.Code = GetShipCode();

            //place the StarShip on the map
            ship.SetLocation(x, y);

            //notify the host
            if (Host != null)
                Host.StarShipCreated(ship);

            //State Change for BuildStarShip
            SubmitStateChange(StateChange.BuildStarShip, x, y, owner, st, engines, primary, cloaked, system, ship.Code);

            return ship;
        }
        private void ShipBuildStarShip(ResourceStarShip rss, int x, int y, StarShipType st, int engines, int primary, bool cloaked)
        {
            if (rss == null)
                return;

            //validate build starship location
            if (!InBounds(x, y))
                return;

            if (GetObjectAt(x, y) != null && !(GetObjectAt(x, y) is UDO))
                return;

            //Deduct resources for ship cost
            int cost = ShipCostCalculator.GetCost(st, engines, primary, cloaked, null);
            if (cost > rss.ResourcesInt)
                return;
            rss.Resources -= cost;
            SubmitStateChange(StateChange.SetShipResources, rss, rss.Resources);

            //create the StarShip instance
            StarShip ship = StarShip.CreateInstance(this, rss.Owner, st, engines, primary, null);
            ship.Cloaked = cloaked;
            ship.Code = GetShipCode();

            //place the StarShip on the map
            ship.SetLocation(x, y);

            //notify the host
            if (Host != null)
                Host.StarShipCreated(ship);

            //State Change for BuildStarShip
            SubmitStateChange(StateChange.BuildStarShip, x, y, rss.Owner, st, engines, primary, cloaked, null, ship.Code);
        }

        //Is a specific order target still valid
        private bool TargetValid(SVMapObject svm, Player p)
        {
            if (svm is StarSystem)
                return true;
            if (!svm.VisibleTo[p])
                return false;
            if (svm is StarShip)
            {
                StarShip ship = svm as StarShip;
                return !ship.Destroyed;
            }
            else
                return true;
        }

        //Pass changes to scenario data to clients
        internal void SetScenarioData(string key, string value)
        {
            SubmitStateChange(StateChange.ScenarioData, key, value);
        }

        //IDynamicEvents implementation
        #region IDynamicEvents Members

        //Display a message to the client
        void IDynamicEvents.DisplayMessage(string message)
        {
            if (Host != null)
                Host.DisplayMessage(message);
            SubmitStateChange(StateChange.DisplayMessage, message);
        }

        //Create an explosion
        void IDynamicEvents.CreateExplosion(int x, int y, int particles, SCGColor startColor, SCGColor endColor, int startDiam, int endDiam, int lifeSpan, bool playSound)
        {
            if (Host != null)
                Host.CreateExplosion(x, y, particles, startColor, endColor, startDiam, endDiam, lifeSpan, playSound);
            SubmitStateChange(StateChange.Explosion, x, y, particles, startColor, endColor, startDiam, endDiam, lifeSpan, playSound);
        }

        //Relocate an object
        void IDynamicEvents.RelocateObject(SVMapObject svm, int x, int y)
        {            
            SCGPoint pt = new SCGPoint(x, y);
            if (svm.Location == pt)
                return;
            pt = ClosestEmptyPoint(pt);
            bool adj = svm.IsAdjacent(pt);
            svm.SetLocation(pt.X, pt.Y);
            if (Host != null)
            {
                if (adj)
                    Host.ObjectMoved(svm, (int)pt.X, (int)pt.Y, 10);
                else
                    Host.ObjectRelocated(svm);
            }
            SubmitStateChange(StateChange.ObjectRelocated, svm, (int)pt.X, (int)pt.Y);
        }

        //Set a terrain cell
        void IDynamicEvents.SetTerrain(int x, int y, char terrain)
        {
            if (!InBounds(x, y))
                return;
            if (GetTerrain(x, y) != terrain)
            {
                SetTerrain(x, y, terrain);
                if (Host != null)
                    Host.TerrainChanged(x, y, terrain);
                SubmitStateChange(StateChange.SetTerrain, x, y, terrain);                
            }
        }

        //Move a Starship toward the destination point
        void IDynamicEvents.MoveStarShip(StarShip ship, int destX, int destY)
        {
            MoveShipTowardPoint(ship, new SCGPoint(destX, destY));
        }

        //Remove an object
        void IDynamicEvents.RemoveObject(SVMapObject svm)
        {
            RemoveObject(svm);
            SubmitStateChange(StateChange.RemoveObject, svm);
        }

        //Adjust starship values
        void IDynamicEvents.AdjustShipValues(StarShip ship, int? engines, int? maxEngines, int? value, int? maxValue, bool respectFixedValues)
        {
            if (!respectFixedValues || ship.ShipType.GetFixedEngines() == 0)
            {
                if (engines != null)
                    if (engines > 0)
                        ship.Engines = (int)engines;
                if (maxEngines != null)
                    if (maxEngines > 0)
                        ship.MaxEngines = (int)maxEngines;
            }
            if (!respectFixedValues || ship.ShipType.GetPrimarySystemName() != "")
            {
                if (value != null)
                    if (value > 0)
                        ship.Value = (int)value;
                if (maxValue != null)
                    if (maxValue > 0)
                        ship.MaxValue = (int)maxValue;
                maxValue = 1;
            }
            SubmitStateChange(StateChange.AdjustShipValues, ship, ship.Engines, ship.MaxEngines, ship.Value, ship.MaxValue);
        }

        //adjust starsystem values
        public void AdjustStarSystemValues(StarSystem sys, int? value, double? resources, int? shields, int? scanners)
        {
            if (value != null)
                sys.Value = (int)value;
            if (resources != null)
                sys.Resources = (double)resources;
            if (shields != null)
                sys.Shields = (int)shields;
            if (scanners != null)
                sys.Scanners = (int)scanners;
            SubmitStateChange(StateChange.AdjustSystemValues, sys, sys.Value, sys.Resources, sys.Shields, sys.Scanners);
        }

        //Create a StarShip
        StarShip IDynamicEvents.CreateStarShip(int x, int y, Player owner, StarShipType shipType, int engines, int primary, bool cloaked)
        {
            SVMapObject svm = GetObjectAt(x, y);
            if (svm != null && !(svm is UDO))
            {
                SCGPoint pt = ClosestEmptyPoint(new SCGPoint(x, y));
                x = (int)pt.X;
                y = (int)pt.Y;
            }
            return BuildStarShip(null, owner, x, y, shipType, engines, primary, cloaked);
        }

        //Set system shields
        void IDynamicEvents.SetShields(StarSystem system, int shields)
        {
            system.Shields = shields;
            SubmitStateChange(StateChange.SetShields, system, shields);
        }

        //Move/relocate a UDO
        void IDynamicEvents.MoveUDO(UDO udo, int x, int y)
        {
            MoveUDO(udo, x, y);
        }

        //Create a StarSystem
        StarSystem IDynamicEvents.CreateStarSystem(int x, int y, int value, int shields, int scanners, double resources)
        {
            StarSystem sys = new StarSystem(this);
            SetToClosestEmpty(ref x, ref y);
            sys.SetLocation(x, y);
            sys.Value = value;
            sys.Shields = shields;
            sys.Scanners = scanners;
            sys.Resources = resources;

            //generate random name  
            sys.Name = Scenario.NameGenerator.RandomName;

            //alert client
            if (Host != null)
                Host.StarSystemCreated(sys);
            SubmitStateChange(StateChange.CreateStarSystem, x, y, value, shields, scanners, resources, sys.Name);          

            return sys;
        }

        //Create a WormHole
        Wormhole IDynamicEvents.CreateWormhole(int x, int y, int destX, int destY)
        {
            SetToClosestEmpty(ref x, ref y);
            Wormhole wh = new Wormhole(this);
            wh.SetLocation(x, y);
            wh.DestX = destX;
            wh.DestY = destY;
            if (Host != null)
                Host.MapObjectCreated(wh);
            SubmitStateChange(StateChange.CreateWormhole, wh.X, wh.Y, wh.DestX, wh.DestY);
            return wh;
        }

        //Create a BlackHole
        BlackHole IDynamicEvents.CreateBlackHole(int x, int y, int radius)
        {
            SetToClosestEmpty(ref x, ref y);
            BlackHole bh = new BlackHole(this);
            bh.Radius = radius;
            bh.SetLocation(x, y);
            if (Host != null)
                Host.MapObjectCreated(bh);
            SubmitStateChange(StateChange.CreateBlackHole, bh.X, bh.Y, radius);
            return bh;
        }

        //Create a Pulsar
        Pulsar IDynamicEvents.CreatePulsar(int x, int y)
        {
            SetToClosestEmpty(ref x, ref y);
            Pulsar p = new Pulsar(this);
            p.SetLocation(x, y);
            if (Host != null)
                Host.MapObjectCreated(p);
            SubmitStateChange(StateChange.CreatePulsar, p.X, p.Y);
            return p;
        }

        //Set a Black Hole Radius
        void IDynamicEvents.SetBlackHoleRadius(BlackHole bh, int radius)
        {
            bh.Radius = radius;
            SubmitStateChange(StateChange.SetBlackHoleRadius, bh, radius);
        }

        //Damage a StarShip
        void IDynamicEvents.DamageStarShip(StarShip ship, int damage)
        {
            ship.Damage(damage, true);           
        }

        //Pause the action
        void IDynamicEvents.Pause()
        {
            if (Host != null)
                Host.Pause();
        }

        //Create a UDO
        void IDynamicEvents.CreateUDO(int x, int y, string name, string imageFile, string description, bool autoPickup, bool ignoreBlackHoles, bool taggable)
        {
            SetToClosestEmpty(ref x, ref y);
            UDO udo = new UDO(this, name, imageFile);
            udo.Description = description;
            udo.AutoPickUp = autoPickup;
            udo.IgnoreBlackHoles = ignoreBlackHoles;
            udo.IsTaggable = taggable;
            udo.SetLocation(x, y);
            SubmitStateChange(StateChange.CreateUDO, x, y, name, imageFile, description, autoPickup, ignoreBlackHoles, taggable);
            if (Host != null)
                Host.UDOCreated(udo);
        }

        #endregion
    }

    //Level of alliance
    public enum AllianceLevel { None, NonAggression, Visibility, Formal };
}
